<?php

namespace app\models\tableModel;

use app\entity\PageListEntity;
use app\models\cacheModel\AdminRoleModel as AdminRoleCacheModel;
use app\models\tableModel\AdminRoleModel as AdminRoleTableModel;
use app\models\tableModel\base\AdminUser;
use app\service\tool\ConvertService;
use app\service\tool\FileService;
use app\service\tool\GenerateService;
use app\service\tool\UtilsService;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as ReadExcel;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as WriteExcel;
use yii\db\Expression;
use yii\db\ExpressionInterface;
use yii\helpers\ArrayHelper;

/**
 * 操作员管理[Model]
 * @property $importTotalPage
 * User: Administrator
 * Date: 2020/09/19
 * Time: 10:11:31
 * @property string $password
 */
class AdminUserModel extends AdminUser
{

    /**
     * 使用新密码字段的场景
     */
    const addUser = 'add_user';

    /**
     * 状态 列表
     * @var array
     */
    private static $statusList = [
        [
            'key'   => 'open',
            'value' => 1,
            'text'  => '已开启'
        ],
        [
            'key'   => 'disabled',
            'value' => -1,
            'text'  => '已禁用'
        ],
    ];

    /**
     * 导出的配置
     * @var array
     */
    private static $exportConfig = [
        'field' => [ // 字段列表
            'id'        => '用户编号',
            'user_name' => '用户名',
            'nick_name' => '昵称',
            'add_time'  => '时间',
        ]
    ];

    /**
     * 导入的配置
     * @var array
     */
    private static $importConfig = [
        'field' => [ // 字段列表
            'id'        => '用户编号',
            'user_name' => '用户名',
            'nick_name' => '昵称',
            'add_time'  => '时间',
        ]
    ];

    /**
     * 新密码
     * @var string
     */
    public $password;

    /**
     * 新密码
     * @var string
     */
    public $importTotalPage = 0;

    /**
     * 暂存角色列表
     * @var AdminRoleCacheModel
     */
    protected $roleList;

    /**
     * 规则验证
     * @return array
     */
    public function rules()
    {

        $parent = parent::rules();
        // 状态
        $statusList = array_column(self::getStatList(), 'value');

        return ArrayHelper::merge($parent, [
            [['status'], 'in', 'range' => $statusList, 'message' => '状态不合法'],
            [['role_id'], 'checkRoleId'],
            [['importTotalPage'], 'safe'],
            [['password'], 'required', 'on' => self::addUser],
        ]);
    }

    /**
     * 重写label的 文字
     */
    public function attributeLabels()
    {

        $parent = parent::attributeLabels();
        return array_merge($parent, [
            'id'              => '编号',
            'add_time'        => '添加时间',
            'update_time'     => '更新时间',
            'status'          => '状态',
            'password'        => '密码',
            'importTotalPage' => '导入分页总页数'
        ]);
    }

    /**
     * 场景
     * @return array
     */
    public function scenarios()
    {
        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            // 使用密码的场景
            self::addUser => [
                'password'
            ]
        ]);
    }

    /**
     * 检测角色是否合法
     * @param null $attributeNames
     * @param bool $clearErrors
     * @return bool
     */
    public function checkRoleId($attributeNames = null, $clearErrors = true)
    {

        $roleInfo = AdminRoleModel::loadModel($this->$attributeNames);
        if (!$roleInfo) {
            $this->addError('role_id', '找不到角色信息');
            return false;
        }

        if ($roleInfo['status'] != AdminRoleModel::getStatusOpen()) {
            $this->addError('role_id', '该角色已禁用');
            return false;
        }

        return true;
    }

    /**
     * 自定义过滤单条[where]
     *  ` 仅限于通过方法[loadWhere]传入的条件, 可以在其中自定义逻辑
     *  ` 保证最终返回值为一维数组[Yii2]通用条件格式[运算符, 字段名, 值]
     * @param $condition array [sql]查询条件, 严格遵循Yii多条件查询:[运算符, 字段名, 值]
     * @return array
     */
    public function whereCustomFilter($condition = [])
    {

        // // 例：字段为编号 —— 操作符使用[IN]
        // if ($field == 'id') {
        //     return ['IN', $condition[1], $value[2]];
        // }

        // 最终返回必须为数组：[运算符, 字段名, 值]，符合Yii2规范
        return $condition;
    }

    /**
     * 获取分页信息
     * @param $page
     * @param $limit
     * @param null $field
     * @return PageListEntity
     */
    public function getPaginate($page, $limit, $field = null)
    {

        // 条件
        $where = $this->where;

        // 当前页面计算
        $page = max($page - 1, 0);

        // 查找的 字段空的 就默认给列表
        if (!$field) $field = '*';

        // 基础 where加载完毕
        $base = $this::find()->select($field)->where($where);

        // 数据的获取 分页等
        $list = $base->offset($page * $limit)
            ->limit($limit)
            ->asArray()->all();
        // 总条数
        $total = $base->count();

        // $roleIdList = array_column($list, 'role_id');
        // 角色列表
        $this->roleList = AdminRoleTableModel::loadModel()
            ->loadWhere([
                'AND',
                ['=', 'status', AdminRoleTableModel::getStatusOpen()]
            ])->getPaginate(1, 200)->getList();
        $this->roleList = array_column($this->roleList, null, 'id');

        // 格式化数据
        foreach ($list as $k => &$v) {

            // 更新时间
            if (!empty($v['update_time'])) {
                $v['update_time_text']   = date('Y-m-d H:i:s', $v['update_time']);
                $v['update_time_text_s'] = date('Y-m-d', $v['update_time']);
            }

            // 状态文本
            if (isset($v['status'])) {
                $v['status_text'] = self::getStatusText($v['status']);
            }

            // 角色编号
            if (isset($v['role_id']) && !empty($this->roleList[$v['role_id']]['name'])) {
                $v['role_name'] = $this->roleList[$v['role_id']]['name'];
            } else {
                $v['role_name'] = '--';
            }
        }

        return PageListEntity::loadModel(compact('list', 'total'));
    }

    /**
     * 添加|保存
     * @param bool $runValidation 是否仅仅运行验证，不运行保存
     * @param string[]|null|string $attributeNames 仅仅保存某几个字段
     * @return bool
     */
    public function save($runValidation = false, $attributeNames = null)
    {

        $nowTime = time();
        // 添加的话要赋值一些初始数据
        if (empty($this->id)) {

            // 可以是走[mongoId]
            $this->id = GenerateService::newMongoId();
            // 添加时间
            $this->add_time      = $nowTime;
            $this->status        = self::getStatOpen(); // 状态
            $this->salt          = GenerateService::newMongoId(); // 加密字符串
            $this->avatar        = empty($this->avatar) ? '/images/thumb/user03.png' : $this->avatar; // 默认头像
            $this->password_hash = self::getPassCrypt($this->password, $this->salt); // 密码
        }

        // 更新时间
        $this->update_time = $nowTime;

        ### 检测
        if ($this->hasErrors() || !$this->validate($attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[操作员管理员表]操作模型]验证数据失败                   ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        ### 需要 && 执行保存
        if (!$runValidation && !parent::save(false, $attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[操作员管理员表]操作模型]保存数据失败                  ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        // 密码生成
        return true;
    }

    /**
     * 是否为超管
     * @param $userId string|int|bool 用户编号
     * @return bool
     */
    public function isSuper($userId = false)
    {

        $roleId = 0;
        ### 如需要参数查询用户信息
        if ($userId !== false) {
            $userId = self::loadModel($userId);
            $roleId = $userId ? $userId->getAttribute('role_id') : false;
        }

        ### 不走参数 直接获取当前记录角色编号
        if ($userId === false) $roleId = $this->getAttribute('role_id');

        return AdminRoleModel::isSuper($roleId);
    }

    /**
     * 导出到[excel]文件
     * @param $filePath
     * @param $records
     * @return bool
     */
    public function exportExcel($filePath, $records)
    {

        ### 判断结果
        if (empty($records)) return true;

        try {

            ### 执行表格
            // 配置
            $config = self::$exportConfig['field'];
            // 实例化
            $spreadsheet = new Spreadsheet();
            // 获取活动工作薄
            $sheet = $spreadsheet->getActiveSheet();
            // 当前此次添加开始sheet数
            if (file_exists($filePath)) {
                $reader = new ReadExcel();
                $index  = $reader->load($filePath)->getActiveSheetIndex();
            } else {
                $index = 1;
                ### 先添加标头
                $i = 0;
                foreach ($config as $k => $v) {

                    // 最终数据
                    $sheet->getCell(chr(65 + $i) . '1')->setValue(
                        $v
                    )->getStyle()->applyFromArray([
                        'font'      => [
                            'bold' => true,
                        ],
                        'alignment' => [
                            'horizontal' => Alignment::HORIZONTAL_LEFT, // 水平居中
                        ],
                    ]);
                    $i++;
                }
            }

            ### 添加数据
            // 循环表格
            $i = 1;
            foreach ($records as $item) {
                $ci = 0; // 字段增加
                foreach ($config as $k2 => $v2) {
                    if (empty($item[$k2])) continue;

                    // 时间格式化
                    if (strstr($k2, 'time')) {
                        $item[$k2] = date('Y-m-d H:i:s', $item[$k2]);
                    }

                    // 获取单元格
                    $cellA = $sheet->getCell(chr(65 + $ci) . ($index + $i));
                    $cellA->getStyle()->applyFromArray([
                        'alignment' => [
                            'horizontal' => Alignment::HORIZONTAL_LEFT,
                        ],
                    ]);
                    // 给单元格赋值
                    $cellA->setValue($item[$k2]);
                    $ci++;
                }
                $i++;
            }

            // 先建立下文件夹
            FileService::mkdir(dirname($filePath));
            // Xlsx类 将电子表格保存到文件
            $writer = new WriteExcel($spreadsheet);
            $writer->save($filePath);

            return true;
        } catch (\Exception $error) {

            $this->addError(500, $error->getMessage());
            return false;
        }
    }

    /**
     * 导入Excel
     * @param $filePath string 文件路径
     * @param $page int 第几页
     * @param $pageSize int 分页大小
     * @return bool
     */
    public function importExcel($filePath, $page, $pageSize)
    {

        try {

            ### 执行表格
            // 配置
            $config = self::$importConfig['field'];
            // 当前此次添加开始sheet数
            $activeSheet = IOFactory::load($filePath)->getActiveSheet();
            // 总行数
            $total = $activeSheet->getHighestRow();
            // 此文件总页数赋值
            $importTotalPage = ConvertService::toUpNumber($total / $pageSize, 0);
            $this->setAttributes(['importTotalPage' => $importTotalPage]);
            // 此次操作最大行
            $maxLine = ($page * $pageSize);
            // 此次操作的最小行
            $miniLine = ($page * $pageSize - ($pageSize - 2));
            if ($miniLine > $total) {
                $this->addError(404, '没有此页数据');
                return false;
            }

            ### 添加数据
            // 数据塑造
            $addData = [];
            for ($i = $miniLine; $i <= $maxLine; $i++) {
                // 当前行数大于总数
                if ($i > $total) break;
                $row     = [];
                $configI = 0;
                foreach ($config as $k => $v) {
                    $row[$k] = $activeSheet->getCell(chr(65 + $configI) . $i)->getCalculatedValue();
                    $configI = $configI + 1;
                }
                $addData[] = $row;
            }
            // 执行添加 - 只有要添加数据不为空
            $result = !empty($addData) ? self::createData($addData) : false;
            if (!$result) {
                $this->addError(500, '数据提交保存失败');
                return false;
            }

            return true;
        } catch (\Exception $error) {

            $this->addError(500, $error->getMessage());
            return false;
        }
    }


    /**
     * 生成新密码
     * @param $passStr
     * @param $salt
     * @return string
     */
    public static function getPassCrypt($passStr, $salt)
    {
        return md5($passStr . $salt);
    }

    /**
     * [静态方法]批量快速更新某些字段
     * @param $condition
     * @param array $fieldVal
     * @return bool
     */
    public static function updateField($condition, $fieldVal = [])
    {

        // 验证字段
        $model = self::loadModel();
        $model->load($fieldVal, '');
        if (!$model->validate(array_keys($fieldVal))) {
            $error = UtilsService::getModelError($model->getErrors());
            self::$error_[$error['column']] = [$error['msg']];
            return false;
        }
        // 重新取值规则化后的特定值列表
        $fieldVal = $model->getAttributes(array_keys($fieldVal));

        $db = \Yii::$app->db->createCommand();

        try {

            $db->update(self::tableName(), $fieldVal, $condition)->execute();

            // 否则成功
            return true;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [操作员管理]批量修改[指定字段]失败，   ``",
                "``         {$error->getMessage()}                       ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }

    /**
     * 批量添加数据
     * @param array $createData
     * @return bool
     */
    public static function createData($createData = [])
    {

        $db = \Yii::$app->db->createCommand();
        try {

            ### 先数据格式化
            // 如果是一维数组则二维数组化
            if (ArrayHelper::isAssociative($createData)) {
                $createData = [$createData];
            }
            $values = [];
            foreach ($createData as $k => $v) {

                $model = self::loadModel();
                $model->load($createData[$k], '');
                if (!$model->save(true)) {
                    // 取出错误信息
                    $error = UtilsService::getModelError($model->errors);
                    // 添加到静态方法上
                    self::$error_[$error['column']] = [$error['msg']];
                    return false;
                }

                $createData[$k] = $model->getAttributes($model::getTableSchema()->getColumnNames());

                // 循环一些数据
                foreach ($createData[$k] as $kc => $vc) {
                    // 字段类型为[JSON]类型需要转为[JSON]
                    if (is_array($vc)) {
                        $createData[$k][$kc] = json_encode($vc, JSON_UNESCAPED_UNICODE);
                        continue;
                    }
                }

                // 值赋值
                $values[] = array_values($createData[$k]);;
            }

            ### 取出此次操作的字段列表
            $columns = !current($createData) ? [] : array_keys(current($createData));

            // 执行
            $addResult = $db->batchInsert(self::tableName(), $columns, $values)->execute();

            return $addResult;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([

                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [操作员管理]批量添加[数据]失败，      ``",
                "``         {$error->getMessage()}                       ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }


    /**
     * 获取[正常]状态 值
     * @return mixed|string
     */
    public static function getStatOpen()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['open']['value'];
    }

    /**
     * 获取[禁用]状态 值
     * @return mixed|string
     */
    public static function getStatDisabled()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['disabled']['value'];
    }

    /**
     * 获取[状态]列表 值
     * @return mixed|string
     */
    public static function getStatList()
    {

        // 最终正常返回
        return self::$statusList;
    }

    /**
     * 获取[状态]文本
     * @param $value
     * @return mixed|string
     */
    public static function getStatusText($value)
    {

        // 列表
        $list = array_column(self::$statusList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }

    /**
     * 获取静态错误
     * @return mixed
     */
    public static function getStaticErrors()
    {
        return self::$error_;
    }

    /**
     * 获取成员变量[$roleList]
     * @return AdminRoleCacheModel
     */
    public function getRoleList()
    {
        return $this->roleList;
    }
}
